#include "../../common.hpp"
#include <arpa/inet.h>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <netinet/in.h>
#include <poll.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <unistd.h>

const size_t PORT = 8085;
const int MAX_CLIENT = 2048;

class poll_server {
public:
    static poll_server &get_instance() {
        static poll_server ss;
        return ss;
    }

    bool init() {
        _listen_fd = create_and_listen(PORT);
        if (_listen_fd <= 0) {
            std::cerr << "create_and_listen error";
            return false;
        }
        _fds[0].fd = _listen_fd;
        _fds[0].events = POLLIN;
        _nfds = 1;
        return true;
    }

    void run() {
        while (true) {
            if (poll(_fds, _nfds, -1) < 0) {
                std::cerr << "poll error" << std::endl;
                return;
            }
            if (_fds[0].revents & POLLIN) {
                struct sockaddr_in client_addr;
                socklen_t client_len = sizeof(client_len);
                int conn_fd = accept(_listen_fd, (struct sockaddr *) &client_addr, &client_len);
                if (conn_fd == -1) {
                    std::cerr << "accept error" << std::endl;
                } else {
                    char client_ip[INET_ADDRSTRLEN];
                    inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, client_len);
                    std::cout << "New connection is from " << client_ip << ":" << ntohs(client_addr.sin_port) << std::endl;

                    int flags = fcntl(conn_fd, F_GETFL, 0);
                    fcntl(conn_fd, F_SETFL, flags | O_NONBLOCK);

                    if (_nfds < MAX_CLIENT) {
                        _fds[_nfds].fd = conn_fd;
                        _fds[_nfds].events = POLLIN;
                        _nfds++;
                    } else {
                        std::cerr << "Too many clients" << std::endl;
                        close(conn_fd);
                    }
                }
            }

            for (int i = 1; i < _nfds; ++i) {
                if (_fds[i].revents & POLLIN) {
                    char buffer[1024];
                    memset(buffer, 0, sizeof(buffer));
                    int n = read(_fds[i].fd, buffer, sizeof(buffer) - 1);
                    if (n <= 0) {
                        if (n == 0) {
                            std::cout << "client fd " << _fds[i].fd << "disconnented" << std::endl;
                            close(_fds[i].fd);
                            for (int j = i; j < _nfds - 1; ++j) {
                                _fds[j] = _fds[j + 1];
                            }
                            --_nfds;
                            --i;
                        } else {
                            std::cerr << "read error" << std::endl;
                        }
                    } else {
                        std::cout << "Recevied from fd " << _fds[i].fd << ":" << buffer << std::endl;
                        write(_fds[i].fd, buffer, n);
                    }
                }
            }
        }
    }

private:
    poll_server() = default;
    poll_server(const poll_server &ss) = delete;
    poll_server &operator=(const poll_server &ss) = delete;
    ~poll_server() {
        close(_listen_fd);
    }

private:
    struct pollfd _fds[MAX_CLIENT];
    int _listen_fd;
    int _nfds;
};

int main() {
    poll_server &ps = poll_server::get_instance();
    if (ps.init()) {
        ps.run();
    }

    return 0;
}